#include "command_config.h"
#include "../inc/logger.h"
#include <thread>

FunctionConfig::FunctionConfig()
{
    data1_ = new Data();
	data2_ = new Data();
	curData = data1_;
        
    Register(curData,0, MsgExpress::ErrMessage::default_instance().GetTypeName().c_str());
	Register(curData, 0, MsgExpress::CommonResponse::default_instance().GetTypeName().c_str());
	Register(curData, 0, MsgExpress::PublishData::default_instance().GetTypeName().c_str());
	Register(curData, 0, MsgExpress::LoginInfo::default_instance().GetTypeName().c_str());
	Register(curData, 0, MsgExpress::LoginResponse::default_instance().GetTypeName().c_str());
	Register(curData, 0, MsgExpress::Logout::default_instance().GetTypeName().c_str());
	Register(curData, 0, MsgExpress::HeartBeat::default_instance().GetTypeName().c_str());
	Register(curData, 0, MsgExpress::HeartBeatResponse::default_instance().GetTypeName().c_str());
	Register(curData, 0, MsgExpress::SimpleSubscription::default_instance().GetTypeName().c_str());
	Register(curData, 0, MsgExpress::SubscribeData::default_instance().GetTypeName().c_str());
	Register(curData, 0, MsgExpress::UnSubscribeData::default_instance().GetTypeName().c_str());
	Register(curData, 0, MsgExpress::ComplexSubscribeData::default_instance().GetTypeName().c_str());
	Register(curData, 0, MsgExpress::GetAppList::default_instance().GetTypeName().c_str());
	Register(curData, 0, MsgExpress::AppList::default_instance().GetTypeName().c_str());
	Register(curData, 0, MsgExpress::GetAppInfo::default_instance().GetTypeName().c_str());
	Register(curData, 0, MsgExpress::AppInfo::default_instance().GetTypeName().c_str());
	Register(curData, 0, MsgExpress::UpdateAppStatus::default_instance().GetTypeName().c_str());
	Register(curData, 0, MsgExpress::AppServerList::default_instance().GetTypeName().c_str());
	Register(curData, 0, MsgExpress::BrokerInfo::default_instance().GetTypeName().c_str());
	Register(curData, 0, MsgExpress::RegisterService::default_instance().GetTypeName().c_str());

	Register(curData, 1, Gateway::CommonResponse::default_instance().GetTypeName().c_str());
	Register(curData, 1, Gateway::Login::default_instance().GetTypeName().c_str());
	Register(curData, 1, Gateway::Logout::default_instance().GetTypeName().c_str());
	Register(curData, 1, Gateway::Subscribe::default_instance().GetTypeName().c_str());
	Register(curData, 1, Gateway::SubscribeResult::default_instance().GetTypeName().c_str());
}

FunctionConfig::~FunctionConfig()
{
    delete data1_;
	delete data2_;
}


int FunctionConfig::Hashcode(const char* str)
{
	int h = 0;
	const char *p = str;
	for (int i = 0; i < strlen(str); i++)
	{
		h = 31 * h + (*p);
		p++;
	}
	return h;
}
int FunctionConfig::GetCommand(const char* classname)
{
	if (curData->mapClass2Command.find(classname) != curData->mapClass2Command.end())
		return curData->mapClass2Command[classname];
	else
		return Hashcode(classname);
	return 0;
}
string FunctionConfig::GetClass(int command)
{
	map<int, string>::const_iterator it = curData->mapCommand2Class.find(command);
	if (it != curData->mapCommand2Class.end())
		return it->second;
	else
		LOG4_ERROR("cant find class name for command,command=%d", command);
	return "";
}
int FunctionConfig::GetServiceId(const char* classname)
{
	map<string, int>::const_iterator it = curData->mapClass2Service.find(classname);
	if (it != curData->mapClass2Service.end())
		return it->second;
	else
		LOG4_ERROR("cant find serviceid,class=%d", classname);
	return 0;
}
int FunctionConfig::GetServiceId(int command)
{
	map<int, string>::const_iterator it = curData->mapCommand2Class.find(command);
	if (it == curData->mapCommand2Class.end())
	{
		LOG4_ERROR("cant find class name for command,command=%d", command);
	}
	else
	{
		return GetServiceId(it->second.c_str());
	}
	return 0;
}
void FunctionConfig::CopyData(Data* src, Data* dest)
{
	dest->Clear();
	src->CopyTo(dest);
}
void FunctionConfig::Register(Data* data, int serviceid, const char* classname)
{
	int command = Hashcode(classname);

	map<int, string>::const_iterator it = data->mapCommand2Class.find(command);
	map<string, int>::const_iterator it2 = data->mapClass2Service.find(classname);
	if (it != data->mapCommand2Class.end() && it->second != string(classname))
	{
		LOG4_ERROR("Hashcode confict,class:%s", classname);
	}
	else if (it2 != data->mapClass2Service.end() && it2->second != serviceid)
	{
		LOG4_ERROR("Class:%s has two services", classname);
	}
	else
	{
		data->mapCommand2Class.insert(std::make_pair(command, classname));
		data->mapClass2Command.insert(std::make_pair(classname, command));
		data->mapClass2Service.insert(std::make_pair(classname, serviceid));
	}
}
void FunctionConfig::Register(int serviceid, const vector<string>& vecClassname)
{
	std::this_thread::sleep_for(std::chrono::milliseconds(10));
	std::unique_lock<mutex> lock(mtx);
	Data* pData = curData != data1_ ? data1_ : data2_;
	CopyData(curData, pData);
	for (int i = 0; i < vecClassname.size();i++)
		Register(pData, serviceid, vecClassname[i].c_str());
	curData = pData;
}
void FunctionConfig::Register(std::shared_ptr< MsgExpress::RegisterService> registerservice)
{
	std::this_thread::sleep_for(std::chrono::milliseconds(10));
	std::unique_lock<mutex> lock(mtx);
	Data* pData = curData != data1_ ? data1_ : data2_;
	CopyData(curData, pData);
	int serviceid = registerservice->serviceid();
	for (int i = 0; i < registerservice->functions_size(); i++)
	{
		Register(pData,serviceid, registerservice->functions(i).c_str());
	}
	map<int, std::shared_ptr< MsgExpress::RegisterService>>::const_iterator it = pData->mapServiceInfo.find(serviceid);
	if (it != pData->mapServiceInfo.end())
		pData->mapServiceInfo.erase(it);
	pData->mapServiceInfo.insert(std::make_pair(serviceid, registerservice));
	LOGGER_INFO("Register service:" << registerservice->ShortDebugString());
	curData = pData;
}
map<int, std::shared_ptr< MsgExpress::RegisterService>> FunctionConfig::GetAllServiceInfo()
{
	std::unique_lock<mutex> lock(mtx);
	return curData->mapServiceInfo;
}

